using System;

namespace Fadd.Components
{
    /// <summary>
    /// Used to create and maintain components.
    /// </summary>
    /// <seealso cref="ComponentFactory"/>
    public interface IComponentManager : IComponentProvider
    {
        /// <summary>
        /// Add a private component.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Private components are created like regular components when their dependencies are met, 
        /// the difference between private and regular components are that they are not accessible from anywhere.
        /// </para>
        /// <para>
        /// Private components are typically used to instantiate components that are not used anywhere 
        /// but still need to be created when their dependencies have been met.
        /// </para>
        /// </remarks>
        /// <typeparam name="InterfaceType">Interface type, used to request components.</typeparam>
        /// <typeparam name="InstanceType">Instance type, used to create component.</typeparam>
        /// <param name="constructorParameters">Extra parameters used in the constructor.</param>
        void AddPrivate<InterfaceType, InstanceType>(params Parameter[] constructorParameters);

        /// <summary>
        /// Add a private component.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Private components are created like regular components when their dependencies are met, 
        /// the difference between private and regular components are that they are not accessible from anywhere.
        /// </para>
        /// <para>
        /// Private components are typically used to instantiate components that are not used anywhere 
        /// but still need to be created when their dependencies have been met.
        /// </para>
        /// </remarks>
        /// <typeparam name="InterfaceType">Interface type, used to request components.</typeparam>
        /// <typeparam name="InstanceType">Instance type, used to create component.</typeparam>
        /// <param name="constructorParameters">Extra parameters used in the constructor.</param>
        /// <param name="componentCreated">A delegate to be invoked when the private component has been instantiated, will be passed the interface of the created component</param>
        void AddPrivate<InterfaceType, InstanceType>(Action<object> componentCreated, params Parameter[] constructorParameters);

        /// <summary>
        /// Add a private component.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Private components are created like regular components when their dependencies are met, 
        /// the difference between private and regular components are that they are not accessible from anywhere.
        /// </para>
        /// <para>
        /// Private components are typically used to instantiate components that are not used anywhere 
        /// but still need to be created when their dependencies have been met.
        /// </para>
        /// </remarks>
		/// <param name="interfaceType">Interface type, used to request components.</param>
		/// <param name="instanceType">Instance type, used to create component.</param>
		/// <param name="constructorParameters">Extra parameters used in the constructor.</param>
        void AddPrivate(Type interfaceType, Type instanceType, params Parameter[] constructorParameters);


        /// <summary>
        /// Add an component
        /// </summary>
		/// <param name="interfaceType">Interface type, used to request components.</param>
		/// <param name="instanceType">Instance type, used to create component.</param>
        /// <param name="constructorParameters">Extra parameters used in the constructor.</param>
        /// <exception cref="ArgumentException"></exception>
        void AddType(Type interfaceType, Type instanceType, params Parameter[] constructorParameters);

		/// <summary>
		/// Add an existing instance.
		/// </summary>
		/// <param name="interfaceType">The type the instance should be added as</param>
		/// <param name="instance">The instance to add</param>
        void AddInstance(Type interfaceType, object instance);

        /// <summary>
        /// Checks whether a certain component is created or not.
        /// </summary>
        /// <param name="interfaceType">Interface to check</param>
        /// <returns>true if component have been created.</returns>
        bool IsCreated(Type interfaceType);

        /// <summary>
        /// Gets if a component have been created or not.
        /// </summary>
        /// <param name="name">Component name.</param>
        /// <returns>true if component have been created.</returns>
        bool IsCreated(string name);

        /// <summary>
        /// Gets a component
        /// </summary>
        /// <param name="interfaceType">Type of component to get.</param>
        /// <returns>Component if found; otherwise null.</returns>
        object this[Type interfaceType] { get; }

        /// <summary>
        /// Gets a component
        /// </summary>
        /// <param name="name">Name of component to get.</param>
        /// <returns>Component if found; otherwise null.</returns>
        object this[string name] { get; }
    }

    /// <summary>
    /// Used to specify a name for a component.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    public sealed class ComponentNameAttribute : Attribute
    {
        private readonly string _name;

		/// <summary>
		/// Initializes a new instance of the <see cref="ComponentNameAttribute"/> class.
		/// </summary>
		/// <param name="name">Name of the component.</param>
        public ComponentNameAttribute(string name)
        {
            Check.Require(name, "name");
            _name = name;
        }

        /// <summary>
        /// Name of the component.
        /// </summary>
        public string Name
        {
            get { return _name; }
        }
    }
}
